package edu.hubu.talentmanagementsystem.service;

import com.wf.captcha.utils.CaptchaUtil;
import edu.hubu.talentmanagementsystem.auth.JwtGrantedAuthority;
import edu.hubu.talentmanagementsystem.auth.JwtToken;
import edu.hubu.talentmanagementsystem.auth.JwtUtils;
import edu.hubu.talentmanagementsystem.auth.Payload;
import edu.hubu.talentmanagementsystem.common.CommonResult;
import edu.hubu.talentmanagementsystem.common.RedisUtils;
import edu.hubu.talentmanagementsystem.common.RoleType;
import edu.hubu.talentmanagementsystem.mapper.context.MapperContext;
import edu.hubu.talentmanagementsystem.model.ext.Admin;
import edu.hubu.talentmanagementsystem.model.ext.Company;
import edu.hubu.talentmanagementsystem.model.ext.Role;
import edu.hubu.talentmanagementsystem.model.ext.Staff;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author moonlan
 * date 2021/3/24 上午10:31
 */
@SuppressWarnings(value = "unused")
@Service
public class LoginService {

    private final IAdminService adminService;
    private final IStaffService staffService;
    private final ICompanyService companyService;
    private final BCryptPasswordEncoder encoder;
    private final AuthenticationManager authenticationManager;
    private final RedisUtils redisUtils;

    public LoginService(IAdminService adminService, IStaffService staffService, ICompanyService companyService,
                        BCryptPasswordEncoder encoder, AuthenticationManager authenticationManager,
                        RedisUtils redisUtils) {
        this.adminService = adminService;
        this.staffService = staffService;
        this.companyService = companyService;
        this.encoder = encoder;
        this.authenticationManager = authenticationManager;
        this.redisUtils = redisUtils;
    }

    public CommonResult login(String username, String password, String code, HttpServletRequest request) {
        if (!CaptchaUtil.ver(code, request)) {
            throw new BadCredentialsException("验证码错误");
        }

        Optional<Admin> optionalAdmin = adminService.selectByNum(username);
        if (optionalAdmin.isPresent()) {
            Admin admin = optionalAdmin.get();
            String adminPwd = admin.getAdminPwd();
            if (encoder.matches(password, adminPwd)) {
                Role adminRole = admin.getRole();
                Payload payload = new Payload().setUserName(admin.getAdminNum())
                        .setRoleName(RoleType.valueOf(adminRole.getRoleName().toUpperCase()))
                        .setAuthorities(adminRole.getPermissions()
                                .stream().map(permission -> new JwtGrantedAuthority(permission.getPermissionName()))
                                .collect(Collectors.toList()));
                String token = JwtUtils.getToken(payload);
                JwtToken jwtToken = new JwtToken(token, payload.getAuthorities());
                Authentication authenticate = authenticationManager.authenticate(jwtToken);
                SecurityContextHolder.getContext().setAuthentication(authenticate);
                HashMap<String, Object> map = new HashMap<>();
                map.put("result", admin);
                map.put("token", token);
                redisUtils.addToken(token);
                return CommonResult.success(map);
            }
        }

        Optional<Staff> optionalStaff = staffService.selectByNum(username);
        if (optionalStaff.isPresent()) {
            Staff staff = optionalStaff.get();
            String staffPwd = staff.getStaffPwd();
            if (encoder.matches(password, staffPwd)) {
                Role staffRole = staff.getRole();
                Payload payload = new Payload().setUserName(staff.getStaffNum())
                        .setRoleName(RoleType.valueOf(staffRole.getRoleName().toUpperCase()))
                        .setAuthorities(staffRole.getPermissions()
                                .stream().map(permission -> new JwtGrantedAuthority(permission.getPermissionName()))
                                .collect(Collectors.toList()));
                String token = JwtUtils.getToken(payload);
                JwtToken jwtToken = new JwtToken(token, payload.getAuthorities());
                Authentication authenticate = authenticationManager.authenticate(jwtToken);
                SecurityContextHolder.getContext().setAuthentication(authenticate);
                HashMap<String, Object> map = new HashMap<>();
                map.put("result", staff);
                map.put("token", token);
                redisUtils.addToken(token);
                return CommonResult.success(map);
            }
        }

        Optional<Company> optionalCompany = companyService.selectByNum(username);
        if (optionalCompany.isPresent()) {
            Company company = optionalCompany.get();
            String companyAdminPwd = company.getCompanyAdminPwd();
            if (encoder.matches(password, companyAdminPwd)) {
                Payload payload = new Payload().setUserName(company.getCompanyNum())
                        .setRoleName(RoleType.valueOf(company.getRole().getRoleName().toUpperCase()))
                        .setAuthorities(company.getRole().getPermissions()
                                .stream().map(permission -> new JwtGrantedAuthority(permission.getPermissionName()))
                                .collect(Collectors.toList()));
                String token = JwtUtils.getToken(payload);
                JwtToken jwtToken = new JwtToken(token, payload.getAuthorities());
                Authentication authenticate = authenticationManager.authenticate(jwtToken);
                SecurityContextHolder.getContext().setAuthentication(authenticate);
                HashMap<String, Object> map = new HashMap<>();
                map.put("result", company);
                map.put("token", token);
                redisUtils.addToken(token);
                return CommonResult.success(map);
            }
        }
        return CommonResult.forbidden();
    }

    public CommonResult logout(HttpServletRequest request, HttpServletResponse response) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null) {
            JwtToken jwtToken = (JwtToken) authentication;
            redisUtils.forbidToken(jwtToken.getCredentials().toString());
            return CommonResult.success(null);
        }

        throw new BadCredentialsException("认证失败");
    }
}
